Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java: File constructor path sanitizer #18504

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

jcogs33
Copy link
Contributor

@jcogs33 jcogs33 commented Jan 16, 2025

Description

Adds a path injection sanitizer for the child argument of a java.io.File constructor when that argument is normalized or checked for the absence of "..".

Consideration

Note that sanitizing the second argument of the File constructor also sanitizes other uses of that variable after the constructor call (see the MISSING cases in the tests). Let me know if there is some way to adjust the QL to avoid also sanitizing these other uses. I can sanitize the File constructor call instead of the second argument, but then the first argument needs to be checked for taint.

Pull Request checklist

All query authors

Internal query authors only

  • Changes are validated at scale (internal access required).

@github-actions github-actions bot added the Java label Jan 16, 2025
@jcogs33 jcogs33 force-pushed the jcogs33/java/file-constructor-path-sanitizer branch from 4dc7562 to 7837ad6 Compare January 31, 2025 19:26
@jcogs33 jcogs33 force-pushed the jcogs33/java/file-constructor-path-sanitizer branch from 8ef7637 to 60cc16c Compare February 4, 2025 22:52
@jcogs33 jcogs33 marked this pull request as ready for review February 5, 2025 01:49
@Copilot Copilot bot review requested due to automatic review settings February 5, 2025 01:49
@jcogs33 jcogs33 requested a review from a team as a code owner February 5, 2025 01:49
@jcogs33 jcogs33 requested a review from owen-mc February 5, 2025 01:49

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

Comments suppressed due to low confidence (1)

java/ql/test/library-tests/pathsanitizer/Test.java:482

  • Several consecutive blocks (lines 480–610) repeat similar path validation logic. Consider extracting this shared logic into a utility method or parameterized test to simplify and reduce duplication.
File f1 = new File("safe/file.txt");

Tip: Copilot code review supports C#, Go, Java, JavaScript, Markdown, Python, Ruby and TypeScript, with more languages coming soon. Learn more

Copy link
Contributor

@owen-mc owen-mc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to sanitize the constructor call instead, with checks on the first argument.

src.asExpr().(MethodCall).getMethod().getName() = "source"
}

predicate isSink(DataFlow::Node sink) { exists(Call call | sink.asExpr() = call.getAnArgument()) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worry this could be a performance problem - finding all global flow to arguments of calls. Better to make it more specific - something like any(ConstructorCall constrCall | constrCall.getConstructedType() instanceof TypeFile).getArgument(0).

Copy link
Contributor Author

@jcogs33 jcogs33 Feb 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm also concerned about that, which is why I'm currently running DCA.
Based on previous experimentation with this, I don't think restricting to ConstructorCall will necessarily be any better, but I'll give it a try if my current DCA experiment shows bad performance.

Do we have any better/pre-built way of checking whether an expression is tainted that doesn't require making a global TaintTracking config?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, performance is bad. Running DCA on the ConstructorCall version to see if there's a difference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ConstructorCall version still has bad performance (although not as bad as the Call version).

Comment on lines +365 to +366
// for InlineFlowTest
src.asExpr().(MethodCall).getMethod().getName() = "source"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine for testing, but we shouldn't commit this. I'm sure we can find some other solution, like adding a model to the inlineflowtest so that source is a remote flow source.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify what you mean by "adding a model to the inlineflowtest"? I was thinking of using an actual ActiveThreatModelSource in this test case instead of (File) source();. Is that what you mean? Or did you have something else in mind?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant you could add a model for source as a remote flow source that applies just to this test (if the test is x.ql then the model has to be in a file called x.ext.yml in the same folder). That way the inline flow test can use its way of finding sources (calls to "source") and this flow config can use its way (active threat model). An alternative approach is to change the test to use ActiveThreatModel for its sources, which I think is what you were suggesting. Both are fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants